﻿using ModbusRTULib;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using thinger.DataConvertLib;

namespace ModbusProject
{
    public enum StoreArea
    {
        输出线圈0x,
        输入线圈1x,
        输出寄存器4x,
        输入寄存器3x
    }
    public partial class FrmModbusRTU : Form
    {
        public FrmModbusRTU()
        {
            InitializeComponent();
            InitParam();
        }

        /// <summary>
        /// ModbusRTU通信对象
        /// </summary>
        private ModbusRTU modbus = new ModbusRTU();

        /// <summary>
        /// 当前的连接状态
        /// </summary>
        private bool isConnected = false;

        private DataFormat dataFormat = DataFormat.ABCD;

        #region 初始化参数
        /// <summary>
        /// 初始化参数
        /// </summary>
        private void InitParam()
        {
            //获取本机的端口号列表
            string[] portList = SerialPort.GetPortNames();

            if(portList.Length > 0)
            {
                this.cmb_Port.Items.AddRange(portList);
                this.cmb_Port.SelectedIndex = 0;
            }

            //波特率
            this.cmb_BaudRate.Items.AddRange(new string[] { "2400", "4800", "9600", "19200", "38400" });
            this.cmb_BaudRate.SelectedIndex = 2;

            //校验位
            this.cmb_Parity.DataSource = Enum.GetNames(typeof(Parity));
            this.cmb_Parity.SelectedIndex = 0;

            //停止位
            this.cmb_StopBits.DataSource = Enum.GetNames(typeof(StopBits));
            this.cmb_StopBits.SelectedIndex = 1;

            //数据位
            this.cmb_DataBits.Items.AddRange(new string[] { "7", "8" });
            this.cmb_DataBits.SelectedIndex = 1;

            //大小端
            this.cmb_DataFormat.DataSource = Enum.GetNames(typeof(DataFormat));
            this.cmb_DataFormat.SelectedIndex = 0;

            //存储区
            this.cmb_StoreArea.DataSource = Enum.GetNames(typeof(StoreArea));
            this.cmb_StoreArea.SelectedIndex = 0;

            //数据类型
            this.cmb_DataType.DataSource = Enum.GetNames(typeof(DataType));
            this.cmb_DataType.SelectedIndex = 0;

            //动态修改ListView第二列的宽度
            this.lst_Info.Columns[1].Width = this.lst_Info.Width - this.lst_Info.Columns[0].Width - 25;
        }
        #endregion

        #region 建立连接与断开连接
        private void btn_Connect_Click(object sender, EventArgs e)
        {
            if(isConnected)
            {
                AddLog(1,"ModbusRTU已经连接");
                return;
            }
            Parity parity = (Parity)Enum.Parse(typeof(Parity), this.cmb_Parity.Text, true);
            StopBits stopBits = (StopBits)Enum.Parse(typeof(StopBits), this.cmb_StopBits.Text, true);
            isConnected = modbus.Connect(this.cmb_Port.Text, Convert.ToInt32(this.cmb_BaudRate.Text),parity,Convert.ToInt32(this.cmb_DataBits.Text),stopBits);
            if(isConnected)
            {
                AddLog(0,"Modbus连接成功");
            }
            else
            {
                AddLog(2, "Modbus连接失败");
            }
        }

        private void btn_DisConnect_Click(object sender, EventArgs e)
        {
            modbus.DisConnect();
            isConnected = false;
            AddLog(3, "Modbus断开连接");
        }
        #endregion

        #region 通用写入日志的方法
        /// <summary>
        /// 通用写入日志的方法
        /// </summary>
        /// <param name="level"></param>
        /// <param name="info"></param>
        private void AddLog(int level, string info)
        {
            ListViewItem lst = new ListViewItem(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), level);

            lst.SubItems.Add(info);

            //让最新的数据在最上面
            this.lst_Info.Items.Insert(0, lst);
        }
        #endregion

        #region 通用读取方法
        private void btn_Read_Click(object sender, EventArgs e)
        {
            if(CommonVerity())
            {
                byte devId = byte.Parse(this.txt_SlaveId.Text.Trim());
                ushort start = ushort.Parse(this.txt_Start.Text.Trim());
                ushort length = ushort.Parse(this.txt_Length.Text.Trim());

                DataType dataType = (DataType)Enum.Parse(typeof(DataType), this.cmb_DataType.Text,true);
                StoreArea storeArea = (StoreArea)Enum.Parse(typeof(StoreArea), this.cmb_StoreArea.Text, true);

                dataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), this.cmb_DataFormat.Text, true);

                switch (dataType)
                {
                    case DataType.Bool:
                        ReadBool(storeArea, devId, start, length);
                        break;
                    case DataType.Byte:
                        ReadByte(storeArea, devId, start, length);
                        break;
                    case DataType.Short:
                        ReadShort(storeArea, devId, start, length);
                        break;
                    case DataType.UShort:
                        ReadUShort(storeArea, devId, start, length);
                        break;
                    case DataType.Int:
                        ReadInt(storeArea, devId, start, length);
                        break;
                    case DataType.UInt:
                        ReadUInt(storeArea, devId, start, length);
                        break;
                    case DataType.Float:
                        ReadFloat(storeArea, devId, start, length);
                        break;
                    case DataType.Double:
                    case DataType.Long:
                    case DataType.ULong:
                    case DataType.String:
                    case DataType.ByteArray:
                    case DataType.HexString:
                        AddLog(1, "读取失败，暂不支持该类型");
                        return;
                }
            }
        }
        #endregion

        #region 读取布尔
        private void ReadBool(StoreArea storeArea,byte devId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                    result = modbus.ReadOutputCoils(devId, start, length);
                    break;
                case StoreArea.输入线圈1x:
                    result = modbus.ReadInputCoils(devId, start, length);
                    break;
                case StoreArea.输出寄存器4x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "读取失败，不支持该存储区");
                    return;
            }

            if(result != null)
            {
                AddLog(0, "读取成功：" +StringLib.GetStringFromValueArray(BitLib.GetBitArrayFromByteArray(result, 0, length)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }
        #endregion

        #region 读取字节
        private void ReadByte(StoreArea storeArea, byte devId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                    result = modbus.ReadOutputCoils(devId, start, length);
                    break;
                case StoreArea.输入线圈1x:
                    result = modbus.ReadInputCoils(devId, start, length);
                    break;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(devId, start, length);
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(devId, start, length);
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功：" + StringLib.GetStringFromValueArray(result));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }
        #endregion

        #region 读取short
        private void ReadShort(StoreArea storeArea, byte devId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                    AddLog(1, "读取失败，暂不支持该类型");
                    return;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(devId, start, length);
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(devId, start, length);
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功：" + StringLib.GetStringFromValueArray(ShortLib.GetShortArrayFromByteArray(result)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }
        #endregion

        #region 读取ushort
        private void ReadUShort(StoreArea storeArea, byte devId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                    AddLog(1, "读取失败，暂不支持该类型");
                    return;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(devId, start, length);
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(devId, start, length);
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功：" + StringLib.GetStringFromValueArray(UShortLib.GetUShortArrayFromByteArray(result)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }
        #endregion

        #region 读取Int
        private void ReadInt(StoreArea storeArea, byte devId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                    AddLog(1, "读取失败，暂不支持该类型");
                    return;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(devId, start, (ushort)(length * 2));
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(devId, start, (ushort)(length * 2));
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功：" + StringLib.GetStringFromValueArray(IntLib.GetIntArrayFromByteArray(result,this.dataFormat)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }
        #endregion

        #region 读取UInt
        private void ReadUInt(StoreArea storeArea, byte devId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                    AddLog(1, "读取失败，暂不支持该类型");
                    return;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(devId, start, (ushort)(length * 2));
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(devId, start, (ushort)(length * 2));
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功：" + StringLib.GetStringFromValueArray(UIntLib.GetUIntArrayFromByteArray(result, this.dataFormat)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }
        #endregion

        #region 读取Float
        private void ReadFloat(StoreArea storeArea, byte devId, ushort start, ushort length)
        {
            byte[] result = null;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                    AddLog(1, "读取失败，暂不支持该类型");
                    return;
                case StoreArea.输出寄存器4x:
                    result = modbus.ReadOutputRegisters(devId, start, (ushort)(length * 2));
                    break;
                case StoreArea.输入寄存器3x:
                    result = modbus.ReadInputRegisters(devId, start, (ushort)(length * 2));
                    break;
            }

            if (result != null)
            {
                AddLog(0, "读取成功：" + StringLib.GetStringFromValueArray(FloatLib.GetFloatArrayFromByteArray(result, this.dataFormat)));
            }
            else
            {
                AddLog(1, "读取失败，请检查参数是否正确");
            }
        }
        #endregion

        #region 通用的验证方法
        private bool CommonVerity()
        {
            if(isConnected == false)
            {
                AddLog(1, "请检查是否连接正常");
                return false;
            }
            if (byte.TryParse(this.txt_SlaveId.Text, out _) == false)
            {
                AddLog(1, "请检查站地址格式是否正确");
                return false;
            }
            if(ushort.TryParse(this.txt_Start.Text, out _) == false)
            {
                AddLog(1, "请检查起始地址格式是否正确");
                return false;
            }
            if (ushort.TryParse(this.txt_Length.Text, out _) == false)
            {
                AddLog(1, "请检查长度格式是否正确");
                return false;
            }
            return true;
        }
        #endregion

        #region 通用写入方法
        private void btn_write_Click(object sender, EventArgs e)
        {
            if (CommonVerity())
            {
                byte devId = byte.Parse(this.txt_SlaveId.Text.Trim());
                ushort start = ushort.Parse(this.txt_Start.Text.Trim());

                DataType dataType = (DataType)Enum.Parse(typeof(DataType), this.cmb_DataType.Text, true);
                StoreArea storeArea = (StoreArea)Enum.Parse(typeof(StoreArea), this.cmb_StoreArea.Text, true);

                string setValue = this.txt_WriteValue.Text.Trim();

                dataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), this.cmb_DataFormat.Text, true);

                switch (dataType)
                {
                    case DataType.Bool:
                        WriteBool(storeArea, devId, start,setValue);
                        break;
                    case DataType.Byte:
                        WriteByte(storeArea, devId, start, setValue);
                        break;
                    case DataType.Short:
                        WriteShort(storeArea, devId, start, setValue);
                        break;
                    case DataType.UShort:
                        WriteUShort(storeArea, devId, start, setValue);
                        break;
                    case DataType.Int:
                        WriteInt(storeArea, devId, start, setValue);
                        break;
                    case DataType.UInt:
                        WriteUInt(storeArea, devId, start, setValue);
                        break;
                    case DataType.Float:
                        WriteFloat(storeArea, devId, start, setValue);
                        break;
                    case DataType.Double:
                    case DataType.Long:
                    case DataType.ULong:
                    case DataType.String:
                    case DataType.ByteArray:
                    case DataType.HexString:
                        AddLog(1, "写入失败，暂不支持该类型");
                        return;
                }
            }
        }
        #endregion

        #region 写入布尔
        private void WriteBool(StoreArea storeArea, byte devId,ushort start,string setValue)
        {
            bool result = false;
            switch (storeArea)
            {
                case StoreArea.输出线圈0x:
                    bool[] values = BitLib.GetBitArrayFromBitArrayString(setValue);
                    if(values.Length == 1)
                    {
                        result = modbus.PreSetSingleCoil(devId, start, values[0]);
                    }
                    else
                    {
                        result = modbus.PreSetMultiCoils(devId, start, values);
                    }
                    break;
                case StoreArea.输入线圈1x:
                case StoreArea.输出寄存器4x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败，不支持该存储区");
                    return;
            }
            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败，请检查");
            }
        }
        #endregion

        #region 写入字节
        private void WriteByte(StoreArea storeArea, byte devId, ushort start, string setValue)
        {
            bool result = false;
            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    result = modbus.PreSetMultiRegisters(devId, start, ByteArrayLib.GetByteArrayFromHexString(setValue));
                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败，不支持该存储区");
                    return;
            }
            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败，请检查");
            }
        }
        #endregion

        #region 写入short
        private void WriteShort(StoreArea storeArea, byte devId, ushort start, string setValue)
        {
            bool result = false;
            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    short[] values = ShortLib.GetShortArrayFromString(setValue);
                    if(values.Length == 1)
                    {
                        result = modbus.PreSetSingleRegister(devId, start, values[0]);
                    }
                    else
                    {
                        result = modbus.PreSetMultiRegisters(devId, start, ByteArrayLib.GetByteArrayFromShortArray(values));
                    }
                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败，不支持该存储区");
                    return;
            }
            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败，请检查");
            }
        }
        #endregion

        #region 写入UShort
        private void WriteUShort(StoreArea storeArea, byte devId, ushort start, string setValue)
        {
            bool result = false;
            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    ushort[] values = UShortLib.GetUShortArrayFromString(setValue);
                    if (values.Length == 1)
                    {
                        result = modbus.PreSetSingleRegister(devId, start, values[0]);
                    }
                    else
                    {
                        result = modbus.PreSetMultiRegisters(devId, start, ByteArrayLib.GetByteArrayFromUShortArray(values));
                    }
                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败，不支持该存储区");
                    return;
            }
            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败，请检查");
            }
        }
        #endregion

        #region 写入Int
        private void WriteInt(StoreArea storeArea, byte devId, ushort start, string setValue)
        {
            bool result = false;
            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    int[] values = IntLib.GetIntArrayFromString(setValue);
                    result = modbus.PreSetMultiRegisters(devId, start, ByteArrayLib.GetByteArrayFromIntArray(values));
                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败，不支持该存储区");
                    return;
            }
            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败，请检查");
            }
        }
        #endregion

        #region 写入UInt
        private void WriteUInt(StoreArea storeArea, byte devId, ushort start, string setValue)
        {
            bool result = false;
            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    uint[] values = UIntLib.GetUIntArrayFromString(setValue);
                    result = modbus.PreSetMultiRegisters(devId, start, ByteArrayLib.GetByteArrayFromUIntArray(values));
                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败，不支持该存储区");
                    return;
            }
            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败，请检查");
            }
        }
        #endregion

        #region 写入Float
        private void WriteFloat(StoreArea storeArea, byte devId, ushort start, string setValue)
        {
            bool result = false;
            switch (storeArea)
            {
                case StoreArea.输出寄存器4x:
                    float[] values = FloatLib.GetFloatArrayFromString(setValue);
                    result = modbus.PreSetMultiRegisters(devId, start, ByteArrayLib.GetByteArrayFromFloatArray(values));
                    break;
                case StoreArea.输出线圈0x:
                case StoreArea.输入线圈1x:
                case StoreArea.输入寄存器3x:
                    AddLog(1, "写入失败，不支持该存储区");
                    return;
            }
            if (result)
            {
                AddLog(0, "写入成功");
            }
            else
            {
                AddLog(1, "写入失败，请检查");
            }
        }
        #endregion
    }
}
